#include "helper.h"
#include "monitor.h"
#include "reg.h"

extern uint32_t instr;
extern char assembly[80];

/* decode I-type instrucion with unsigned immediate 
	src1 : 源寄存器
	src2 : 立即数（16位）
*/
static void decode_imm_type(uint32_t instr) {

	op_src1->type = OP_TYPE_REG;
	op_src1->reg = (instr & RS_MASK) >> (RT_SIZE + IMM_SIZE);
	op_src1->val = reg_w(op_src1->reg);
	
	op_src2->type = OP_TYPE_IMM;
	op_src2->imm = instr & IMM_MASK;
	op_src2->val = op_src2->imm;

	op_dest->type = OP_TYPE_REG;
	op_dest->reg = (instr & RT_MASK) >> (IMM_SIZE);
}

make_helper(lui) {
	decode_imm_type(instr);
	reg_w(op_dest->reg) = (op_src2->val << 16);
	sprintf(assembly, "lui   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
}

make_helper(ori) {
	decode_imm_type(instr);
	reg_w(op_dest->reg) = op_src1->val | op_src2->val;
	sprintf(assembly, "ori   %s,   %s,   0x%04x", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), op_src2->imm);
}

make_helper(andi){
	decode_imm_type(instr);
	reg_w(op_dest->reg) = (op_src1->val & zero_extend_16(op_src2->imm));
	sprintf(assembly, "andi   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
}

make_helper(xori){
	decode_imm_type(instr);
	reg_w(op_dest->reg) = (op_src1->val ^ zero_extend_16(op_src2->imm));
	sprintf(assembly, "xori   %s,   0x%04x", REG_NAME(op_dest->reg), op_src2->imm);
}

make_helper(addi){
	decode_imm_type(instr);
	if(IntegerOverflow(op_src1->val , sign_extend_16(op_src2->imm)) ){
		cp0_w(R_Cause) = (cp0_w(R_Cause) & CP0_CAUSE_EXECCODE_CLEAR_MASK) | CP0_CAUSE_EXECCODE_Ov_MASK;
		cp0_w(R_EPC) = cpu.pc;
		cp0_w(R_Status) = cp0_w(R_Status) | CP0_STATUS_EXL_MASK;
		cpu.pc = EXCEPTION_HANDLER_ADDR;
		sprintf(assembly, "addi: IntegerOverflow");
		return;
	}
	reg_w(op_dest->reg) = (op_src1->val + sign_extend_16(op_src2->imm) );
	sprintf(assembly, "addi	  %s,   %s,   0x%04x", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), op_src2->imm);
}

make_helper(addiu){
	decode_imm_type(instr);
	reg_w(op_dest->reg) = (uint32_t)((int)(op_src1->val) + (int)(sign_extend_16(op_src2->imm)) );
	sprintf(assembly, "addiu	  %s,   %s,   %d", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), (int)(sign_extend_16(op_src2->imm)));
}

make_helper(slti){
	decode_imm_type(instr);
	if( ((int)(op_src1->val)) < ((int)sign_extend_16(op_src2->imm)) ){
		reg_w(op_dest->reg) = 0x00000001;
	}
	else{
		reg_w(op_dest->reg) = 0x00000000;
	}
	sprintf(assembly, "slti	  %s,   %s,   %d", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), op_src2->imm);
}

make_helper(sltiu){
	decode_imm_type(instr);
	if( ((uint32_t)(op_src1->val)) < ((uint32_t)sign_extend_16(op_src2->imm)) ){
		reg_w(op_dest->reg) = 0x00000001;
	}
	else{
		reg_w(op_dest->reg) = 0x00000000;
	}
	sprintf(assembly, "sltiu	  %s,   %s,   %d", REG_NAME(op_dest->reg), REG_NAME(op_src1->reg), op_src2->imm);
}

make_helper(lb){
	decode_imm_type(instr);
	reg_w(op_dest->reg) = sign_extend_8(mem_read(op_src1->val + sign_extend_16(op_src2->imm),1));
	sprintf(assembly, "lb	  %s,  %d(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}

make_helper(lbu){
	decode_imm_type(instr);
	reg_w(op_dest->reg) = zero_extend_16(mem_read(op_src1->val + sign_extend_16(op_src2->imm),1));
	sprintf(assembly, "lb	  %s,  %d(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}

make_helper(lh){
	decode_imm_type(instr);
	uint32_t vaddr = op_src1->val + sign_extend_16(op_src2->imm);
	if(vaddr%2 != 0){
		//记录badVaddr 地址
		cp0_w(R_BadVAddr) = vaddr;
		cp0_w(R_Cause) = (cp0_w(R_Cause) & CP0_CAUSE_EXECCODE_CLEAR_MASK) | CP0_CAUSE_EXECCODE_AdEL_MASK;
		cp0_w(R_EPC) = cpu.pc;
		cp0_w(R_Status) = cp0_w(R_Status) | CP0_STATUS_EXL_MASK;
		cpu.pc = EXCEPTION_HANDLER_ADDR;
		sprintf(assembly, "lh: AddressError Exception ");
		return;
	}
	reg_w(op_dest->reg) = sign_extend_16(mem_read(vaddr, 2));
	sprintf(assembly, "lh	  %s,  %d(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}

make_helper(lhu){
	decode_imm_type(instr);
	uint32_t vaddr = op_src1->val + sign_extend_16(op_src2->imm);
	if(vaddr%2 != 0){
		cp0_w(R_BadVAddr) = vaddr;
		cp0_w(R_Cause) = (cp0_w(R_Cause) & CP0_CAUSE_EXECCODE_CLEAR_MASK) | CP0_CAUSE_EXECCODE_AdEL_MASK;
		cp0_w(R_EPC) = cpu.pc;
		cp0_w(R_Status) = cp0_w(R_Status) | CP0_STATUS_EXL_MASK;
		cpu.pc = EXCEPTION_HANDLER_ADDR;
		sprintf(assembly, "lhu: AddressError Exception ");
		return;
	}
	reg_w(op_dest->reg) = zero_extend_16(mem_read(vaddr, 2));
	sprintf(assembly, "lhu	  %s,  %d(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}

make_helper(lw){
	decode_imm_type(instr);
	uint32_t vaddr = op_src1->val + sign_extend_16(op_src2->imm);
	if(vaddr%4 != 0){
		cp0_w(R_BadVAddr) = vaddr;
		cp0_w(R_Cause) = (cp0_w(R_Cause) & CP0_CAUSE_EXECCODE_CLEAR_MASK) | CP0_CAUSE_EXECCODE_AdEL_MASK;
		cp0_w(R_EPC) = cpu.pc;
		cp0_w(R_Status) = cp0_w(R_Status) | CP0_STATUS_EXL_MASK;
		cpu.pc = EXCEPTION_HANDLER_ADDR;
		sprintf(assembly, "lw: AddressError Exception ");
		return;
	}
	reg_w(op_dest->reg) = mem_read(vaddr, 4);
	sprintf(assembly, "lw	  %s,  %d(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}

make_helper(sb){
	decode_imm_type(instr);
	uint32_t vaddr = (uint32_t) ((int)op_src1->val + (int)sign_extend_16(op_src2->imm));
	mem_write(vaddr, 1, reg_w(op_dest->reg));
	sprintf(assembly, "sb	  %s,  %d(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}

make_helper(sh){
	decode_imm_type(instr);
	uint32_t vaddr = (uint32_t) ((int)op_src1->val + (int)sign_extend_16(op_src2->imm));
	if(vaddr%2 != 0){
		cp0_w(R_BadVAddr) = vaddr;
		cp0_w(R_Cause) = (cp0_w(R_Cause) & CP0_CAUSE_EXECCODE_CLEAR_MASK) | CP0_CAUSE_EXECCODE_AdES_MASK;
		cp0_w(R_EPC) = cpu.pc;
		cp0_w(R_Status) = cp0_w(R_Status) | CP0_STATUS_EXL_MASK;
		cpu.pc = EXCEPTION_HANDLER_ADDR;
		sprintf(assembly, "sh: AddressError Exception ");
		return;
	}
	mem_write(vaddr, 2, reg_w(op_dest->reg));
	sprintf(assembly, "sh	  %s,  %d(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}

make_helper(sw){
	decode_imm_type(instr);
	uint32_t vaddr = (uint32_t) ((int)op_src1->val + (int)sign_extend_16(op_src2->imm));
	if(vaddr%4 != 0){
		cp0_w(R_BadVAddr) = vaddr;
		cp0_w(R_Cause) = (cp0_w(R_Cause) & CP0_CAUSE_EXECCODE_CLEAR_MASK) | CP0_CAUSE_EXECCODE_AdES_MASK;
		cp0_w(R_EPC) = cpu.pc;
		cp0_w(R_Status) = cp0_w(R_Status) | CP0_STATUS_EXL_MASK;
		cpu.pc = EXCEPTION_HANDLER_ADDR;
		sprintf(assembly, "sw: AddressError Exception ");
		return;
	}
	mem_write(vaddr, 4, reg_w(op_dest->reg));
	sprintf(assembly, "sw	  %s,  %d(%s)", REG_NAME(op_dest->reg), op_src2->imm, REG_NAME(op_src1->reg));
}
